Jak włączyć widok / częściowe specyficzne stylizacje w AngularJS

Jaki jest właściwy / akceptowany sposób używania oddzielnych arkuszy stylów dla różnych widoków używanych przez moją aplikację?

Obecnie umieszczam element linku w widoku/częściowym html na górze, ale powiedziano mi, że jest to zła praktyka, mimo że wszystkie nowoczesne przeglądarki go wspierają, ale widzę, dlaczego jest to źle widziane.

Inną możliwością jest umieszczenie osobnych arkuszy stylów w moim indeksie.html ' a head ale chciałbym, żeby ładował arkusz stylów tylko wtedy, gdy jego widok jest ładowany w imię występu.

Czy jest to zła praktyka, ponieważ stylizacja nie będzie działać, dopóki css nie zostanie załadowany z serwera, co prowadzi do szybkiego flashowania nieformatowanej treści w powolnej przeglądarce? Jeszcze tego nie widziałem, chociaż testuję to lokalnie.

Czy istnieje sposób na załadowanie CSS przez obiekt przekazany do $routeProvider.when?

Z góry dzięki!
Author: Jordan.J.D, 2013-03-04

7 answers

Wiem, że to pytanie jest już stare, ale po przeprowadzeniu mnóstwa badań nad różnymi rozwiązaniami tego problemu, myślę, że mogłem wymyślić lepsze rozwiązanie.

UPDATE 1: od czasu opublikowania tej odpowiedzi, dodałem cały ten kod do prostej usługi, którą opublikowałem na GitHub. Repo znajduje się tutaj . Zachęcamy do sprawdzenia, aby uzyskać więcej informacji.

UPDATE 2: ta odpowiedź jest świetna, jeśli wszystko, czego potrzebujesz, to lekki rozwiązanie do przeciągania arkuszy stylów dla Twoich tras. Jeśli potrzebujesz bardziej kompletnego rozwiązania do zarządzania arkuszami stylów na żądanie w całej aplikacji, możesz zamówić projekt AngularCSS Door3. Zapewnia znacznie bardziej drobnoziarnistą funkcjonalność.

Jeśli ktoś w przyszłości będzie zainteresowany, oto co wymyśliłem:]}

1. Tworzenie niestandardowej dyrektywy dla elementu <head>:

app.directive('head', ['$rootScope','$compile',
    function($rootScope, $compile){
        return {
            restrict: 'E',
            link: function(scope, elem){
                var html = '<link rel="stylesheet" ng-repeat="(routeCtrl, cssUrl) in routeStyles" ng-href="{{cssUrl}}" />';
                elem.append($compile(html)(scope));
                scope.routeStyles = {};
                $rootScope.$on('$routeChangeStart', function (e, next, current) {
                    if(current && current.$$route && current.$$route.css){
                        if(!angular.isArray(current.$$route.css)){
                            current.$$route.css = [current.$$route.css];
                        }
                        angular.forEach(current.$$route.css, function(sheet){
                            delete scope.routeStyles[sheet];
                        });
                    }
                    if(next && next.$$route && next.$$route.css){
                        if(!angular.isArray(next.$$route.css)){
                            next.$$route.css = [next.$$route.css];
                        }
                        angular.forEach(next.$$route.css, function(sheet){
                            scope.routeStyles[sheet] = sheet;
                        });
                    }
                });
            }
        };
    }
]);

Niniejsza dyrektywa wykonuje następujące rzeczy:

  1. kompiluje (używając $compile) łańcuch html, który tworzy zestaw znaczników <link /> dla każdego elementu w obiekcie scope.routeStyles za pomocą ng-repeat i ng-href.
  2. dodaje skompilowany zestaw elementów <link /> do znacznika <head>.
  3. następnie używa $rootScope do słuchania zdarzeń '$routeChangeStart'. Dla każdego zdarzenia '$routeChangeStart' przechwyca obiekt "current" $$route (trasę, którą użytkownik ma opuścić) i usuwa jego częściowe pliki css ze znacznika <head>. To także chwyta obiekt" next " $$route (trasę, do której ma się udać użytkownik) i dodaje dowolny z jego częściowych plików css do znacznika <head>.
  4. i ng-repeat część skompilowanego znacznika <link /> obsługuje wszystkie dodawanie i usuwanie arkuszy stylów specyficznych dla strony na podstawie tego, co zostanie dodane do obiektu scope.routeStyles lub usunięte z niego.

Uwaga: wymaga to, aby twój ng-app atrybut był na <html> elemencie, a nie na <body> lub czegokolwiek wewnątrz <html>.

2. Określ, które arkusze stylów należą do których tras przy użyciu $routeProvider:

app.config(['$routeProvider', function($routeProvider){
    $routeProvider
        .when('/some/route/1', {
            templateUrl: 'partials/partial1.html', 
            controller: 'Partial1Ctrl',
            css: 'css/partial1.css'
        })
        .when('/some/route/2', {
            templateUrl: 'partials/partial2.html',
            controller: 'Partial2Ctrl'
        })
        .when('/some/route/3', {
            templateUrl: 'partials/partial3.html',
            controller: 'Partial3Ctrl',
            css: ['css/partial3_1.css','css/partial3_2.css']
        })
}]);

Ten config dodaje własną właściwość css do obiektu, który jest używany do ustawiania trasy każdej strony. Obiekt ten jest przekazywany do każdego zdarzenia '$routeChangeStart' jako .$$route. Więc podczas nasłuchiwania zdarzenia '$routeChangeStart', możemy pobrać właściwość css, którą podaliśmy i dodać/usunąć te znaczniki <link /> w razie potrzeby. Zauważ, że podanie właściwości css na trasie jest całkowicie opcjonalne, ponieważ został pominięty w przykładzie '/some/route/2'. Jeśli trasa nie ma właściwości css, dyrektywa <head> po prostu nie zrobi nic dla tej trasy. Zauważ również, że możesz mieć nawet kilka arkuszy stylów dla każdej trasy, jak w powyższym przykładzie '/some/route/3', gdzie właściwość css jest tablicą względnych ścieżek do arkuszy stylów potrzebnych dla tej trasy.

3. You ' re done Te dwie rzeczy ustawiają wszystko, co było potrzebne i robi to, moim zdaniem, z najczystszym kodem możliwe.

Mam nadzieję, że to pomoże komuś, kto może zmagać się z tym problemem tak samo jak ja.
 147
Author: tennisgent,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-20 15:02:29

@ Tennisgent rozwiązanie jest świetne. Jednak myślę, że jest trochę ograniczona.

Modułowość i hermetyzacja kątowa wykraczają poza trasy. Ze względu na sposób, w jaki sieć zmierza w kierunku rozwoju opartego na komponentach, ważne jest, aby zastosować to również w dyrektywach.

Jak już wiesz, w Angular możemy zawierać szablony (strukturę) i kontrolery (zachowanie) w stronach i komponentach. AngularCSS włącza ostatni brakujący element: dołączanie arkuszy stylów (prezentacja).

Dla pełnego rozwiązania proponuję użyć AngularCSS.

  1. obsługuje Ngroute Angular, Router UI, dyrektywy, kontrolery i usługi.
  2. nie wymaga posiadania ng-app w znaczniku <html>. Jest to ważne, gdy na tej samej stronie działa wiele aplikacji]}
  3. można dostosować, gdzie arkusze stylów są wstrzykiwane: głowa, ciało, selektor Niestandardowy, itp...
  4. obsługuje wstępne ładowanie, utrzymywanie i burzenie pamięci podręcznej
  5. obsługuje media zapytania i optymalizuje ładowanie strony poprzez matchMedia API

Https://github.com/door3/angular-css

Oto kilka przykładów:

Trasy

  $routeProvider
    .when('/page1', {
      templateUrl: 'page1/page1.html',
      controller: 'page1Ctrl',
      /* Now you can bind css to routes */
      css: 'page1/page1.css'
    })
    .when('/page2', {
      templateUrl: 'page2/page2.html',
      controller: 'page2Ctrl',
      /* You can also enable features like bust cache, persist and preload */
      css: {
        href: 'page2/page2.css',
        bustCache: true
      }
    })
    .when('/page3', {
      templateUrl: 'page3/page3.html',
      controller: 'page3Ctrl',
      /* This is how you can include multiple stylesheets */
      css: ['page3/page3.css','page3/page3-2.css']
    })
    .when('/page4', {
      templateUrl: 'page4/page4.html',
      controller: 'page4Ctrl',
      css: [
        {
          href: 'page4/page4.css',
          persist: true
        }, {
          href: 'page4/page4.mobile.css',
          /* Media Query support via window.matchMedia API
           * This will only add the stylesheet if the breakpoint matches */
          media: 'screen and (max-width : 768px)'
        }, {
          href: 'page4/page4.print.css',
          media: 'print'
        }
      ]
    });

Dyrektywy

myApp.directive('myDirective', function () {
  return {
    restrict: 'E',
    templateUrl: 'my-directive/my-directive.html',
    css: 'my-directive/my-directive.css'
  }
});

Dodatkowo możesz skorzystać z usługi $css W przypadku edge cases:

myApp.controller('pageCtrl', function ($scope, $css) {

  // Binds stylesheet(s) to scope create/destroy events (recommended over add/remove)
  $css.bind({ 
    href: 'my-page/my-page.css'
  }, $scope);

  // Simply add stylesheet(s)
  $css.add('my-page/my-page.css');

  // Simply remove stylesheet(s)
  $css.remove(['my-page/my-page.css','my-page/my-page2.css']);

  // Remove all stylesheets
  $css.removeAll();

});
Możesz przeczytać więcej o AngularCSS tutaj:

Http://door3.com/insights/introducing-angularcss-css-demand-angularjs

 33
Author: castillo.io,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-13 18:17:05

Może dodać nowy arkusz stylów do głowy $routeProvider. Dla uproszczenia używam ciągu znaków, ale mogę również utworzyć nowy element łącza lub utworzyć usługę dla arkuszy stylów

/* check if already exists first - note ID used on link element*/
/* could also track within scope object*/
if( !angular.element('link#myViewName').length){
    angular.element('head').append('<link id="myViewName" href="myViewName.css" rel="stylesheet">');
}

Największą zaletą prelodingu na stronie jest to, że wszelkie obrazy tła będą już istniały, a mniej lieklyhood of FOUC

 13
Author: charlietfl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-04 02:45:00

@sz3, dość zabawne dzisiaj musiałem zrobić dokładnie to, co chciałeś osiągnąć: 'załaduj określony plik CSS tylko wtedy, gdy użytkownik ma dostęp' konkretną stronę. Zastosowałem więc powyższe rozwiązanie.

Ale Jestem tu, by odpowiedzieć na twoje ostatnie pytanie.: ' gdzie dokładnie powinienem umieścić kod. Wszelkie pomysły?'

Miałeś rację włączając kod do rozwiąż, ale musisz trochę zmienić format.

Spójrz na kod poniżej:

.when('/home', {
  title:'Home - ' + siteName,
  bodyClass: 'home',
  templateUrl: function(params) {
    return 'views/home.html';
  },
  controler: 'homeCtrl',
  resolve: {
    style : function(){
      /* check if already exists first - note ID used on link element*/
      /* could also track within scope object*/
      if( !angular.element('link#mobile').length){
        angular.element('head').append('<link id="home" href="home.css" rel="stylesheet">');
      }
    }
  }
})

Właśnie przetestowałem i działa dobrze , wstrzykuje html i ładuje mój dom.css 'tylko wtedy, gdy trafiłem na trasę '/ home'.

Pełne wyjaśnienie można znaleźć tutaj , ale zasadniczo rozwiązanie: powinno uzyskać obiekt w formacie

{
  'key' : string or function()
} 

Możesz nazwać "klucz "cokolwiek chcesz - w moim przypadku nazwałem" Styl ".

Wtedy dla wartości masz dwie opcje:

  • If it ' s a string , więc jest to alias dla usługi.

  • Jeśli jest to Funkcja, to jest ona wstrzykiwana i zwracana wartość jest traktowana jako zależność.

Głównym punktem jest to, że kod wewnątrz funkcji zostanie wykonany wcześniej, zanim kontroler zostanie utworzony i wywołane zostanie Zdarzenie $routeChangeSuccess.

Mam nadzieję, że to pomoże.
 4
Author: Denison Luz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-29 12:41:37

Super, dziękuję!! Po prostu musiałem wprowadzić kilka poprawek, aby działał z ui-routerem: {]}

    var app = app || angular.module('app', []);

    app.directive('head', ['$rootScope', '$compile', '$state', function ($rootScope, $compile, $state) {

    return {
        restrict: 'E',
        link: function ($scope, elem, attrs, ctrls) {

            var html = '<link rel="stylesheet" ng-repeat="(routeCtrl, cssUrl) in routeStyles" ng-href="{{cssUrl}}" />';
            var el = $compile(html)($scope)
            elem.append(el);
            $scope.routeStyles = {};

            function applyStyles(state, action) {
                var sheets = state ? state.css : null;
                if (state.parent) {
                    var parentState = $state.get(state.parent)
                    applyStyles(parentState, action);
                }
                if (sheets) {
                    if (!Array.isArray(sheets)) {
                        sheets = [sheets];
                    }
                    angular.forEach(sheets, function (sheet) {
                        action(sheet);
                    });
                }
            }

            $rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams) {

                applyStyles(fromState, function(sheet) {
                    delete $scope.routeStyles[sheet];
                    console.log('>> remove >> ', sheet);
                });

                applyStyles(toState, function(sheet) {
                    $scope.routeStyles[sheet] = sheet;
                    console.log('>> add >> ', sheet);
                });
            });
        }
    }
}]);
 2
Author: CraigM,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-11 15:44:45

Jeśli twój CSS musi być tylko zastosowany do jednego konkretnego widoku, używam tego poręcznego fragmentu w moim kontrolerze:

$("body").addClass("mystate");

$scope.$on("$destroy", function() {
  $("body").removeClass("mystate"); 
});

To doda klasę do mojego znacznika body, gdy stan zostanie załadowany, i usunie ją, gdy stan zostanie zniszczony(np. ktoś zmieni strony). To rozwiązuje mój problem związany z potrzebą zastosowania CSS tylko do jednego stanu w mojej aplikacji.

 0
Author: Matt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-15 12:20:27

'Use strict'; kanciaste.moduł ("app") .bieg( [ '$rootScope', '$ state', '$stateParams', function ($rootScope, $state, $stateParams) { $rootScope.$state = $state; $rootScope.$stateParams = $stateParams; } ] ) .config( [ '$stateProvider', '$urlRouterProvider', function ($stateProvider, $urlRouterProvider) {

            $urlRouterProvider
                .otherwise('/app/dashboard');
            $stateProvider
                .state('app', {
                    abstract: true,
                    url: '/app',
                    templateUrl: 'views/layout.html'
                })
                .state('app.dashboard', {
                    url: '/dashboard',
                    templateUrl: 'views/dashboard.html',
                    ncyBreadcrumb: {
                        label: 'Dashboard',
                        description: ''
                    },
                    resolve: {
                        deps: [
                            '$ocLazyLoad',
                            function($ocLazyLoad) {
                                return $ocLazyLoad.load({
                                    serie: true,
                                    files: [
                                        'lib/jquery/charts/sparkline/jquery.sparkline.js',
                                        'lib/jquery/charts/easypiechart/jquery.easypiechart.js',
                                        'lib/jquery/charts/flot/jquery.flot.js',
                                        'lib/jquery/charts/flot/jquery.flot.resize.js',
                                        'lib/jquery/charts/flot/jquery.flot.pie.js',
                                        'lib/jquery/charts/flot/jquery.flot.tooltip.js',
                                        'lib/jquery/charts/flot/jquery.flot.orderBars.js',
                                        'app/controllers/dashboard.js',
                                        'app/directives/realtimechart.js'
                                    ]
                                });
                            }
                        ]
                    }
                })
                .state('ram', {
                    abstract: true,
                    url: '/ram',
                    templateUrl: 'views/layout-ram.html'
                })
                .state('ram.dashboard', {
                    url: '/dashboard',
                    templateUrl: 'views/dashboard-ram.html',
                    ncyBreadcrumb: {
                        label: 'test'
                    },
                    resolve: {
                        deps: [
                            '$ocLazyLoad',
                            function($ocLazyLoad) {
                                return $ocLazyLoad.load({
                                    serie: true,
                                    files: [
                                        'lib/jquery/charts/sparkline/jquery.sparkline.js',
                                        'lib/jquery/charts/easypiechart/jquery.easypiechart.js',
                                        'lib/jquery/charts/flot/jquery.flot.js',
                                        'lib/jquery/charts/flot/jquery.flot.resize.js',
                                        'lib/jquery/charts/flot/jquery.flot.pie.js',
                                        'lib/jquery/charts/flot/jquery.flot.tooltip.js',
                                        'lib/jquery/charts/flot/jquery.flot.orderBars.js',
                                        'app/controllers/dashboard.js',
                                        'app/directives/realtimechart.js'
                                    ]
                                });
                            }
                        ]
                    }
                })
                 );
 0
Author: rambaburoja,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-10 12:04:39